/*********************************************************************
* 
* S3PICPROG -  PIC programmer
* Copyright (C) 2008 Olivier Ringot <oringot@gmail.com>
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, 
* Boston, MA  02110-1301, USA.
* 
**********************************************************************
* 
* $Revision: $
* $Date: $
* $Source: $
* $Log: $
*
**********************************************************************
* command                 host                            target
* --------------------------------------------------------------------
* core_instr              <0x10> <data0> <data1>          <ack>
*
* read                    <0x20>                          <data0> <ack>
* read32                  <0x21>                          <data0>...<data31> <ack>
*
* write                   <0x30> <data0> <data1>          <ack>
* write_prog              <0x31> <data0> <data1>          <ack>
* write32_prog            <0x32> <data0>...<data31>       <ack>
*
* delay hperiod           <0x80> <data0> <data1>          <ack>
* delay prog              <0x81> <data0> <data1>          <ack>                  
* delay erase             <0x82> <data0> <data1>          <ack>                  
* led                     <0x83> <led>   <dummy>          <ack>
*
* pic pins state          <0x90> <data0> <data1>          <ack>
*
*********************************************************************
*stty -F $SERIAL_DEVICE 115200 -parenb -clocal -crtscts -cstopb -icrnl -ixon -ixoff 
*    -parmrk -onlcr -opost -isig -icanon -echo -echok -iexten -echoe -echok 
*    -echoctl -echoke
*********************************************************************/
#include "icsp.h"

/*******************************************************************************
* 
*******************************************************************************/
int icsp_ttys;

/*******************************************************************************
* 
*******************************************************************************/
unsigned char icsp_read()
{
  unsigned char c;
  send_char(0x20);
  c=get_char();
  check_ack();
  return c;
}

/*******************************************************************************
* 
*******************************************************************************/
unsigned char icsp_read32(char*buffer)
{
  unsigned int i;
  unsigned char c;
  
  send_char(0x21);
  
  for(i=0;i<32;i++)
  {
    buffer[i]=get_char();
  }
  check_ack();
}

/*******************************************************************************
* 
*******************************************************************************/
void icsp_write(unsigned int data)
{
  send_char(0x30);
  send_char(data&0xff);
  send_char((data>>8)&0xff);
  check_ack();
}

/*******************************************************************************
* 
*******************************************************************************/
void icsp_write_prog(unsigned int data)
{
  send_char(0x31);
  send_char(data&0xff);
  send_char((data>>8)&0xff);
  check_ack();
}

/*******************************************************************************
* 
*******************************************************************************/
void icsp_write32_prog(unsigned char *buffer)
{
  unsigned int i;
  
  send_char(0x32);
  
  for(i=0;i<32;i++)
  {
    send_char(buffer[i]);  
  
  }
  check_ack();
}

/*******************************************************************************
* 
*******************************************************************************/
void icsp_write_tblptr(unsigned int tblptr)
{
  /* MOVLW tblptr[23:16] */
  icsp_core_instr(0x0e00 | ((tblptr>>16)&0xff));  
  /* MOVWF TBLPTRU       */
  icsp_core_instr(0x6ef8);
  /* MOVLW tblptr[15:8]  */
  icsp_core_instr(0x0e00 | ((tblptr>>8)&0xff));  
  /* MOVWF TBLPTRH       */
  icsp_core_instr(0x6ef7);
  /* MOVLW tblptr[15:8]  */
  icsp_core_instr(0x0e00 | ((tblptr)&0xff));  
  /* MOVWF TBLPTRH       */
  icsp_core_instr(0x6ef6);
}

/*******************************************************************************
* 
*******************************************************************************/
void icsp_core_instr(unsigned int inst)
{
  send_char(0x10);
  send_char(inst&0xff);
  send_char((inst>>8)&0xff);
  check_ack();
}

/*******************************************************************************
* 
*******************************************************************************/
void icsp_delays(unsigned int hperiod,unsigned int prog,unsigned erase)
{
  /* define clock half-period */
  send_char(0x80);
  send_char(hperiod&0xff);
  send_char((hperiod>>8)&0xff);
  check_ack();

  /* define program delay */
  send_char(0x81);
  send_char(prog&0xff);
  send_char((prog>>8)&0xff);
  check_ack();

  /* define erase delay */
  send_char(0x82);
  send_char(erase&0xff);
  send_char((erase>>8)&0xff);
  check_ack();
}

/*******************************************************************************
* 
*******************************************************************************/
void icsp_enter()
{
  /* enable nMCLR/PGM drivers */
  send_char(0x90);
  send_char(0x00);
  send_char(0x80);
  check_ack();
  
  /* assert PGM (=1) */
  send_char(0x90);
  send_char(0x02);
  send_char(0x80);
  check_ack();
  
  /* dessert nMCRL (=1) */
  send_char(0x90);
  send_char(0x03);
  send_char(0x80);
  check_ack();
}

/*******************************************************************************
* 
*******************************************************************************/
void icsp_exit()
{
  /* assert nMCRL (=0) */
  send_char(0x90);
  send_char(0x02);
  send_char(0x80);
  check_ack();
 
  /* deassert PGM (=0) */
  send_char(0x90);
  send_char(0x00);
  send_char(0x80);
  check_ack();
  
  /* disable nMCLR/PGM drivers */
  send_char(0x90);
  send_char(0x00);
  send_char(0x00);
  check_ack();
}

/*******************************************************************************
*
*******************************************************************************/
void check_ack()
{
  char c;
  c=get_char();
  if(c!=0)
  {
    printf("error: unexpected ACK from target (0x%-.2x) !\n",c&0xff);
    icsp_exit();
    exit(-1);
  }
}

/*******************************************************************************
*
*******************************************************************************/
void send_char(unsigned char c)
{
  int count;
  do
  {
    count=write(icsp_ttys,&c,1);
    if(count==-1)
    {
      printf("error during send_char\n");
      exit(-1);
    }
  }
  while(count==0);
}

/*******************************************************************************
*
*******************************************************************************/
char get_char()
{
  int count;
  unsigned char r;
  do                                        
  {                                         
    count=read(icsp_ttys,&r,1);                
  }
  while(count==0);
  if(count!=1)
  {
    printf("error during get_char (%d,%d)\n",count,errno);
    perror(NULL);
    exit(-1);
  }
  return r;
}
